package com.gee.thread.flow.handler;

import com.gee.thread.flow.common.exception.SkippedException;
import com.gee.thread.flow.common.result.CheckResult;
import com.gee.thread.flow.common.result.ExecuteState;
import com.gee.thread.flow.common.result.WorkResult;
import com.gee.thread.flow.executor.ExecuteContext;
import com.gee.thread.flow.executor.NextWork;
import com.gee.thread.flow.executor.WorkExecuteProperties;
import com.gee.thread.flow.work.AbstractWork;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * desc:
 *
 * @author gee wrote on  2021-01-14 19:06:41
 */
public interface WorkHandler<V> {

    String WHOLE_SKIPPED_CAUSED_BY = "The whole thread flow has been exceptional caused by the work which expects,named ";

    String PREVIOUS_NECESSARY_EXCEPTION = "previous necessary work determined to skip  which named ";

    String ALL_PREVIOUS_UNNECESSARY_EXCEPTION = "all previous unnecessary works determined to skip:  ";

    String FOLLOW_UP_HAS_BEGUN = "the follow-up work has begun which is named ";

    String WHOLE_FUTURE_EXCEPTION = "the whole future executes exceptionally";

    CheckResult check(AbstractWork<V, ?, ?> currentWork, ExecuteContext<V> executeContext);

    void handleBeforeBegin(AbstractWork<V, ?, ?> currentWork, ExecuteContext<V> executeContext);

    void handleAfterFinish(AbstractWork<V, ?, ?> currentWork, ExecuteContext<V> executeContext);

    default void trySkipPrevious(AbstractWork<V,?,?> currentWork, SkippedException skippedException,
                                      ExecuteContext<V> executeContext){
        WorkResult<?,?> preWorkResult = executeContext.getWorkResult(currentWork.getId());
        AtomicInteger executeState = preWorkResult.getExecuteState();
        //set the previous works's execute state from INIT to be EXCEPTIONAL to void execute
        WorkExecuteProperties<V,?> preWorkExecuteProperties = currentWork.getWorkExecuteProperties();
        if (executeState.compareAndSet(ExecuteState.INIT.getCode(), ExecuteState.EXCEPTIONAL.getCode())) {
            preWorkResult.setException(skippedException);
            preWorkResult.setExceptionMsg(skippedException.getMessage());
        /*
            set the previous works's execute state from WORKING to be EXCEPTIONAL and interrupt the thread
            if enableInterrupted
         */
        }else if (executeState.compareAndSet(ExecuteState.WORKING.getCode(), ExecuteState.EXCEPTIONAL.getCode())){
            if (preWorkExecuteProperties.getEnableInterrupted()){
                currentWork.beforeInterrupted();
                Thread asyncThread = preWorkExecuteProperties.getAsyncThread();
                if (asyncThread != null && asyncThread.isAlive()){
                    asyncThread.interrupt();
                }
                Thread selfThread = preWorkExecuteProperties.getSelfThread();
                if (selfThread != null && selfThread.isAlive()){
                    selfThread.interrupt();
                }
            }
            preWorkResult.setException(skippedException);
            preWorkResult.setExceptionMsg(skippedException.getMessage());
        }
        if (preWorkExecuteProperties.getPreWorks().size() > 0){
            preWorkExecuteProperties.getPreWorks().forEach((key, work) -> {
                Map<String, ? extends NextWork<V, ?>> nextWorkWrapperMap = work.getWorkExecuteProperties().getNextWorks();
                if (allNextBegun(nextWorkWrapperMap, executeContext)){
                    trySkipPrevious(work,skippedException,executeContext);
                }
            });
        }
    }

    default boolean allNextBegun(Map<String,? extends NextWork<V,?>> nextWorkWrapperMap, ExecuteContext<V> executeContext){
        if (nextWorkWrapperMap == null || nextWorkWrapperMap.size() == 0) {
            return true;
        }
        return nextWorkWrapperMap.keySet().parallelStream().allMatch(nextWorkId ->
                executeContext.getWorkResult(nextWorkId).getExecuteState().get() > ExecuteState.INIT.getCode()
        );
    }
}
